/**

    1. <!DOCTYPE> 的作用？
        告诉浏览器的解析器用什么文档标准解析这个文档
    2.行内元素？
        (一个行内元素占据它对应标签的边框所包含的空间)行内元素是指一行内可连续并排出现的元素，例如：<span> <a> <i> <b> <input> <button> <select> <textarea>...
    3.块级元素？
        块级元素占据父容器的整个宽度，例如：<div> <ul> <ol> <li> <p> <h1~6>  H5新标签<header> <main> <section>...
    4.行内元素和块级元素的区别？
        格式上：默认情况下，行内元素不会以新行开始，而块级元素会
        内容上：默认情况下，行内元素只能包含文本和其他行内元素，而块级元素可以包含文本和其他行内元素与块级元素
        行内元素和块级元素属性不同，行内元素设置width/height无效
    5.空元素？
        空元素是指中间没有内容的标签，即单标签    <br/> <hr/> <img/> <input> <link> ...
    6.页面导入样式时，使用@import 和 link标签 有什么区别？
        1.@import 是css的语法规则，只有导入样式表的作用;而link既可以导入样式表，还可以设置rel等属性引入网站图表等 
        2.link标签引入的css被同时加载；而用@import 引入的css则需要等到页面加载完毕之后加载
        3.link标签可以被js脚本动态插入改变样式，而@imoprt 不可以
    7.浏览器内核/引擎？
        浏览器的内核引擎分为渲染引擎和JS引擎，
        渲染引擎的功能就是渲染，即在浏览器中展示所请求的内容，默认情况下，渲染引擎可以渲染xml,html,图片等，还可以借助插件渲染pdf
        JS引擎的功能就是解析执行javascript脚本，
    8.常见的浏览器内核？
        IE              Trident
        Chrome          Blink
        Firefox         Gecko
        Safari          Webkit
    9.浏览器渲染过程中遇到js文件怎么处理？
        在构建DOM时，HTML若遇到了javascript脚本，则它会暂停对文档的解析，通过JS引擎解析运行javascript脚本，等待js加载完毕，才会从中断的地方继续加载文档
    10.defer 和 async 的作用和区别？
        JS脚本没有defer和async属性的话，浏览器会立即加载运行javascript
        defer属性表示延迟执行引入的javascript，即这段代码在加载时HTML并未停止解析，这两个过程是并行的
        async属性代表异步执行javascript，与defer的区别在于，如果已经加载好，就会开始执行，也就是说它的执行仍然会阻塞文档的解析，只是它的加载过程中不会阻塞，多个脚本的执行顺序无法保证
    11.渲染页面时常见的不良影响？
        样式闪烁问题： CSS文件大加载缓慢或者放在body底部导致
        白屏：JS脚本放在head头部加载
    12.重绘 和 回流 ？
        重绘：当渲染树中一些元素需要更新属性时，而这些属性只影响元素的外观，风格，而不会影响布局的操作，比如 background color 等称为重绘；
        回流：当渲染树中的一部分或全部因为元素的尺寸 布局 隐藏等改变而需要重新构建的操作，会影响到布局的操作称为回流。
            添加/删除可见的DOM元素
            元素尺寸改变 （margin padding border width height）
            内容变化，（input输入框中输入文字）
            浏览器窗口尺寸改变 -resize事件
            。。。
        回流必引起重绘，重绘不一定会引起回流。
    13.如何减少回流？
         使用transfrom代替top
         不要使用table布局，可能很小的改动都会造成整个table的回流
         不要一条一条的修改DOM样式，与其这样，还不如事先定义好css的class，然后修改DOM的className
    14.HTML5 有哪些新特性？
        1.H5语义化标签<header> <main> <nav> <aside> <section> <footer>...
        2.表单控件：date time email search url
        2.Canvas画布
        3.Video Audio 音视频
        4.本地存储 localStorage
        5.地理定位 geolocation
        6.离线存储 appcache
        7.新的文档属性 document.visibilityState
        8.新的技术 websocket
    15.如何处理HTML5新标签的兼容性问题？
        通过现成第三方库 html5shiv.js / 通过document.createElement()方法创建标签
    16. b和strong 与 i和em的区别？
        b和strong都是粗体，i和em都是斜体
        strong和em具有语义化，强调文本
    17.前端需要注意哪些SEO优化？
        1.合理的title，description，keyword，title值强调重点即可，description把内容高度概括，keyword列举出几个关键字即可
        2.语义化的HTML代码，符合W3C规范
        3.重要内容html代码放在最前面，保证内容肯定被抓取
        4.重要的内容不要用js输出，爬虫不会执行js获取内容
        5.非装饰性的图片必须用alt
        6.提高网站速度：网站速度是搜索引擎排序的一个重要指标
    18.浏览器的存储方式及区别？
        浏览器的存储方式有cookie sessionStorage localStorage
        cookie：最多能存储4k数据，生成时间由expire属性决定，只能被同源的页面访问共享
        sessionStorage：会话存储，能够存储5M大小或更大的数据，随着浏览器的关闭而释放数据，只能被同一个窗口的同源页面所共享访问
        localStorage：本地存储，能够存储5M大小或更大的数据，不会随着浏览器的关闭而清除数据，除非手动清除，否则会一直存在浏览器中，只能被同源的页面访问共享
    19.Lable标签的作用是什么？ 用法？
        label标签用来定义表单控件间的关系，当用户选择该标签时，浏览器会自动将焦点转到和标签相关的表单控件上
            <lable for="name">姓名</lable>
            <input type="text" id="name" />
    20.如何实现浏览器内多个标签页之间的通信？
        1.可以通过websocket协议，因为websocket协议可以实现服务器推送，所以服务器就可以用来当做这个中介者，标签页通过向服务器发送数据，然后服务器向其他标签页推送转发
        2.使用localStorage本地存储方式，可以在一个标签页对localStorage变化事件进行监听，然后当另一个标签页修改数据的时候，就可以通过这个监听事件来获取到数据
    21.webSocket如何解决低版本浏览器兼容性问题？
        1.Adobe Flash Socket
        2.基于长轮询的XHR请求（XmlHttpRequest ajax）
    22.页面可见性（Page Visibility API） 可以有哪些用途？
        这个API的作用在于，通过监听网页的可见性，可以预判卸载网页，还可以用来节省资源，比如：一旦用户不看当前网页，就可以停止对当前网页的轮询，停止网页的动画，音视频等等
    23.实现不用border画出1px高度的线，兼容浏览器标准模式和怪异模式
        {
            overflow:hidden;
            height:1px;
            background-color:red;
        }
    24.title和h1的区别？
        title属性没有明确意义只是表示标题,h1则表示层次明确的标题，对页面信息的抓取也有很大的影响
    25.img的title和alt的区别？
        title通常是鼠标滑入当前元素时所展示的信息，而alt是img的特有属性，为图片无法展示时显示的信息，搜索引擎会重点分析alt属性
    26.Canvas和svg的区别？
        Canvas是一种通过JavaScript绘制2D图形的方法，以像素的形式描绘，所以放缩时会导致锯齿失真的情况
        SVG是一种通过XML绘制的2D图形的语言，当SVG缩放时并不会失真
    27.网页验证码的作用？
        1.区分用户是计算机还是人的公共全自动程序，可以防止恶意破解密码，刷票，论坛灌水
        2.有效防止黑客对某一个特定注册用户用特定程序暴力破解方式进行不断的登录尝试
    28.attribute和property的区别？
        <div id="dv" class="content" data-id="1"></div>
        如上述div元素，可通过 document.getElementById('dv').getAttribute('id') 获取id的值 也可以通过document.getElementById('dv').id 的方式
        前者称为attribute ，后者称为property 
        只要是HTML标签内定义的都是attribute，两者同步，Attribute值是字符串类型
        Property属性则可以看做是DOM对象的键值对，用点操作符对它们进行操作，非自定义attribute，如id、class、titile等，都会有对应的property映射。就算没有在标签中出现都会有空值
        实际编程中，基本的DOM操作都是使用property的点操作符
            只有两种情况不得不使用attribute，
                1.自定义Attribute，因为它不能同步到DOM property （比如上面的div元素的data-id属性）
                2.访问内置的HTML标签的Attribute，这些attribute不能从property上同步过来，(比如input标签的value值)
    29.用于预格式化文本格式的标签是？
        <pre></pre>  保持文本原有的格式
    30.head标签中必不可少的是？
        <head>为头部标签，可以在head标签中使用<link><meta><style><script>等，唯一且必须的标签是<title></title>
    31.主流浏览器内核私有前缀？
        IE          Trident       -ms       
        Chrome      Webkit        -webkit
        Firefox     mozilla       -mos
        Safari      Webkit        -webkit
    32.disabled和readonly的区别？
        disabled指当input元素加载时被禁止使用，此时不会随着表单提交
        readonly规定输入字段为只读状态，会随着表单提交
        无论设置了disabled或readonly，通过js脚本都可以更改input的value值
    33.前端性能优化？
        页面内容方面：
            1.通过文件合并，css精灵图，base64等方式来减少http的请求
            2.通过DNS缓存机制来减少DNS的查询次数
            3.通过设置缓存策略，对常用不变的资源进行缓存
            4.使用延迟加载的方式，来减少页面首屏加载时需要请求的资源
            5.通过用户行为，对某些资源使用预加载的方式来提高用户需要访问资源时的响应速度
        服务器方面：
            1.通过CDN服务来提高用户对资源请求时的响应速度
            2.服务器端通过Gzip等方式对于传输的资源进行压缩，减小文件体积
            3.尽可能减小cookie的大小，静态资源分配到其他域名下，避免对静态资源请求时携带不必要的cookie
        CSS和javascript方面：
            1.通过对css和js文件进行压缩，减小文件体积
            2.css放在head标签中，减少页面的首次渲染时间
            3.避免使用@import 
            4.JS放在body底部，或者使用defer或async属性，避免脚本的加载和执行阻塞页面的渲染
    34.html规范中为什么引用资源不加协议头 http或https ？
        如果用户当前访问的页面是通过https协议预览的，那么网页中的资源也只能够通过https协议的资源进行访问，否则浏览器会出现警告信息，
        不同的浏览器警告信息展示形式不同，为了解决这个问题，可以省略这个协议，省略后浏览器照样能正确引用相应的资源，还节省了5字节的数据量
    35.盒模型？
        盒模型分为内容(content)，填充(padding)，边框(border)，边距(margin)四个部分
        其中有标准盒模型和IE怪异模型
        标准盒模型：width/height 只包含content 不包含 padding和border
        IE怪异模型：width/height 包含content padding border
        可以使用box-sizing:content-box;设置为标准盒模型
    36.css中的选择器有哪些？
        id选择器        #
        类选择器        .
        标签选择器      div
        属性选择器      input[name=""]
        子代选择器      div>p
        后代选择器      div p
        兄弟选择器      li~a    选择li元素之后所有同层级的a元素
        相邻兄弟选择器  li+a     选择紧接着li元素后的a元素
        伪类选择器      :hover,:nth-child        
        伪元素选择器    ::after , ::before       
        通配符选择器    *
    37.伪类和伪元素的区别？
        伪类和伪元素是用来修饰不在文档树中的部分
        伪类用于当已有的元素处于某个状态时，为其添加对应的样式，这个状态是根据用户行为而动态变化的。比如说 当用户悬停在指定位置时，可以通过:hover来描述这个元素的状态
        伪元素用于创建一些不在文档树中的元素，并为其添加样式。比如可以通过::before在一个元素前增加一些文本，虽然用户可以看到这些文本，但是这些文本实际上并不存在与文档树中
        按照规则 :: 代表伪元素    : 代表伪类    虽然目前大多数浏览器都支持使用这两种方式表示伪元素 
    38.css中哪些属性可继承？
        一般具有继承性的属性有：字体相关的属性(font-size,font-weight),文本相关的属性(color,text-align),列表属性(list-style),光标属性cursor,元素可见性visibility
        当一个元素不是可继承属性的时候，可以通过将他的属性值设置成inherit来继承
    39.css优先级？
        !important > 行内样式 > id选择器 > class选择器 > 标签选择器 > *         权重会叠加
    40.css3新增的伪类？
        nth-child()
        last-child
        first-of-type
        last-of-type
        nth-of-type()
    41.如何居中div？
        对于宽高固定的元素：
            1.可以通过margin:0 auto;设置水平居中
            2.{ position:absolute; left:0; right:0; top:0; bottom:0; margin:auto; } 水平垂直居中
            3.{ position:absolute; width:200px; height:200px; left:50%; top:50%; margin:-100px; } 水平垂直居中
            4.{ position:absolute; left:50%; top:50%; transform:translate(-50%,-50%); } 水平垂直居中
            5.{ display:flex; justify-content:center; align-items:center; } 水平垂直居中
        对于宽高不固定的元素，4和5即可实现
    42.display的值有哪些？
        none: 元素隐藏
        block: 转为块级元素，换行显示，可设置宽高
        inline: 转为行内元素，逐行显示，不可设置宽高
        inline-block: 转为行内块元素，同行显示，可设置宽高
        table:块级表格显示
        flex: 伸缩布局
        inherit:继承父元素display的值
    43.position的值定位原点是？
        static：无定位方式，原点不变
        fixed：定位原点左上角
        relative:定位原点位于当前元素左上角
        absolute：定位原点位于父级的relative元素的左上角
    44.css3有哪些新特性？
        C3选择器
        动画 (animation)
        圆角 (border-radius)
        阴影 (box-shadow text-shadow)
        渐变 (gradient)
        transform translate() scale() rotate() skew()
        过渡 (transtion)
    45.用纯css画一个三角形？
        采用相邻边框连接处的均分原理，把元素的宽高设为0，只设置border，把任意三条边隐藏transparent 就是一个三角形了
        {
            width:0;
            height:0;
            border-width:10px;
            border-style:solid;
            border-color:red transparent transparent transparent;
        }
    46.css多列等高实现方式？
        1.使用负margin-bottom 和 正 padding-bottom 对冲实现
            ul{
                overflow:hidden;
            }
            ul>li{
                margin-bottom:-99999px;
                padding-bottom:99999px;
            }
        2.利用flex布局
            ul{
                display:flex;
            }
            ul>li{
                flex:1;
            }
    47.css里的visibility属性里有个collapse属性值是干嘛用的？在不同浏览器中的区别？
        
    48.width:100% 和 width:auto 的区别？
        width:100%:会使当前元素盒子的宽度等于父元素的内容宽度
        width:auto：会使当前元素撑满整个父元素，margin/padding/border/content会自动分配水平空间
    49.简单介绍使用图片base64编码的优缺点？
        优点：可以减少一个http请求
        缺点：1.根据base64编码的原理，编码之后的大小会比原文件大1/3，如果把大图片编码到html/css中，不仅会造成文件体积的增加，还会增加浏览器对html/css文件解析渲染的时间
            2.使用base64无法直接缓存，要缓存只能缓存包含base64的文件，这比直接缓存图标的效果差的多
    50.对于margin重叠问题的理解？

    51.BFC概念的理解？ (BlockFormattingContext:块格式化上下文)
        BFC指的是块级格式化上下文，一个元素形成BFC之后，那么它内部元素产生的布局不会影响到外部元素，外部元素也不会影响到BFC中的内部元素。
        一个BFC就相当于一个隔离区，和其他区域互不影响，一般来说，一个根元素就是一个BFC区域，浮动和绝对定位的元素也会形成BFC，
        display的属性值为inline-block,flex这些属性时也会创建BFC，还有就是overflow值不为visible时都会创建BFC
    52.IFC是什么？
        IFC指的是行级格式化上下文
        行级上下文会在水平方向上，一个接着一个摆放，当一行不够时会自动换行，行级上下文的高度由内部最高的内联盒子的高度决定
    53.为什么需要清除浮动？ 清除浮动的方式？
        因为浮动后的元素，脱离了文档流，高度塌陷，影响到其他元素的布局显示。
        方式：
            1. ::after{ content:""; clear:both; }
            2.父元素添加{ overflow:hidden }
    54.css优化 提高性能的方式？
        加载性能：
            1.使用压缩方式，将css样式文件进行压缩，减小文件体积
            2.使用link的方式引入样式表，减少使用@import 的方式
        选择器性能：
            1.避免使用通配符选择器
            2.尽量少的用标签选择器，而是用class选择器
            3.尽量少的使用后代选择器，降低选择器的权重值。
            4.了解哪些属性是可以通过继承而来的，避免这些属性重复使用规则
        渲染性能：
            1.谨慎使用高性能属性：浮动，定位
            2.尽量减少页面的重绘 重排
            3.去除空规则。{ } 空规则的产生一般来说是为了预留样式
            4.属性值为0时，不添加单位
            5.属性值为浮动小数时，去掉小数点前的0
            6.使用精灵图将多个小图标整合在一次http请求中
    55.浏览器是怎么解析css选择器的？

    56.为什么不建议使用通配符初始化css样式？
        通配符会将网页上所有的标签都遍历一遍，这样大大加强了对网站运行的负载，会使网站加载的时间变长
    57.全屏滚动的原理是什么 用到哪些css属性？
        假设有5个需要展示的页面，那么页面的高度为500%，只是展示100%，容器及容器内部的页面取当前可视区高度，同时容器的父元素overflow属性值为hidden，
        通过更改容器可视区的位置来实现全屏滚动的效果，主要是响应鼠标滚轮事件，页面通过css动画进行移动
        overflow:hidden;    transition
    58.响应式原理？ 兼容ie低版本浏览器？
        响应式原理：媒体查询 @media max-width() ; 可用现成第三方库respond.js兼容ie低版本浏览器
    59.移动端需要设置的meta？
        <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0,user-scalable=no"/>
    60.如何去除inline-block元素间间距？
        1.去除空格
        2.使用margin负值
        3.font-size:0;
    61.overflow:scroll 移动端不能平滑滚动怎么解决？
        { -webkit-overflow-scrolling:touch; }
    62.有一个高度自适应的div，里面有两个div，第一个div高度为100px，希望第二个div占满剩下的高度 如何做？
        <div class="box">
            <div style="height:100px;"></div>
            <div class="ord"></div>
        </div>
            .box{
                position:relative;
            }
            .ord{
                position:absolute;
                top:100px;
                bottom:0;
                left:0;
                right:0;
            }
        2.  
    63.什么是cookie隔离？ 或者说 资源请求的时候不带上cookie怎么做？
        cookie隔离是指将静态资源文件存放在非主域名下，这样请求静态资源时就不会带着cookie
    64.什么是css预处理器/后处理器？
        css预处理器定义了一种新的语言，为css提供了一些编程的特性，然后再编译成正常的css文件，如less ，sass 等
        css后处理器是对css进行处理，并最终生成css的预处理器，如 postcss
    65.画一条0.5px的线
        {
            width:100%;
            height:1px;
            background-color:red;
            transform:scaleY(.5);
        }
    66.transition和animation的区别？
        transition 关注的是css属性的变化，而animation作用于元素本身并不是样式属性，可以使用关键帧的概念，实现更自由的动画
    67.什么是首选最小宽度？
        首选最小宽度指的是元素最适合的最小宽度，中文最小宽度为每个汉字的宽度，英文最小宽度由特定的连续的英文字符决定，
        所以如果想让英文和中文一样，每一个字符都是用最小宽度，则可以设置css { word-break:break-all; }
    68.为什么height:100%;会无效？
        对于普通文档流中的元素，百分比高度要想起作用，其父元素必须有一个可以生效的高度值。
    69.什么是替换元素？
        通过修改某个属性值呈现的内容就可以被替换的元素称为替换元素。如：<img>、<video>、<input>等
    70.如何实现单行、多行文本溢出的省略(...)
        单行溢出：
            p{
                overflow:hidden;
                text-overflow:ellipsis;
                white-space:nowrap;
            }
        多行溢出：
            p{
                position:relative;
                line-height:1.5em;
                height:3em;             两行省略，3/1.5=2
                overflow:hidden;
                word-break:break-all;
            }
            p::after{
                content:'...';
                position:absolute;
                bottom:0;
                right:0;
                background-color:#fff;
            }
    71.常见的元素隐藏方式？
        1.display:none;
        2.opacity:0;
        3.visibility:hidden;
        4.{ position:absolute;left:-9999px;top:-9999px; }
        5.{ position:absolute;z-index:-999px; }
        6.{ width:0;height:0; }
        7.transform:scale(0,0)
    72.css实现上下固定，中间自适应布局？
        1.绝对定位方式：
            header,footer{
                position:absolute;
                height:100px;
                left:0;
                right:0;
            }
            header{
               top:0; 
            }
            main{
                position:absolute;
                top:100px;
                bottom:100px;
            }
            footer{
                bottom:0
            }
        2.使用flex布局：
            body{
                display:flex;
                flex-direction:column;
                height:100vh;
            }
            header,footer{
                height:100px;
                background-color:red
            }
            main{
                flex-grow:1;
                background-color:green;
            }
    73.css实现两栏布局？
        1.浮动实现
            .left{
                float:left;
                width:200px;
            }
            .right{
                margin-left:200px;
                width:auto;
            }
        2.绝对定位
            .left{
                position:absolute;
                left:0;
                width:200px;
            }
            .right{
                position:absolute;
                left:200px;
            }
            或
            .right{
                margin-right:200px;
            }
        3.flex布局
            box{
                display:flex;
            }
            .left{
                width:200px;
            }
            .right{
                flex:1
            }
    74.三栏布局？
        1.左右浮动宽度200px;中间设置margin:0 200px;需要注意的是中间的元素需要放在最底下
        2.父元素相对定位，左右元素绝对定位在两侧设置宽度，中间margin：0 200px;
        3.flex布局，父元素display:flex;左右两元素设置宽度，中间元素flex:1;
    75.js中的数据类型？
        基本数据类型：Number String Boolean undefined null
        引用数据类型：Object Array Function
    76.js中有哪些内置对象？
        JS中的内置对象主要是指程序执行前存在全局作用域里由js定义的一些全局值属性、函数和用来实例化其他对象的构造函数对象。
        一般我们经常用到的全局值属性：NaN、undefined，全局函数如：parseInt(),parseFloat()，用来实例化对象的构造函数如：Date，Object，Math    
    77.undefined与undeclared的区别？
        已在作用域下声明但还没赋值的变量是undefined，还没有在作用域下声明过的变量是undeclared。
        对于undeclared变量的引用，浏览器会报引用错误，但是我们可以通过typeof的安全机制来避免这种报错，因为undeclared的变量typeof会返回undefined
    78.undefined和null的区别？
        首先undefined和null都是基本数据类型，undefined指的是变量已声明但未赋值，null代表的含义为空对象
    79.JavaScript原型 原型链 ？
        个人理解：原型就是一个属性，这个属性同时也是一个对象，构造函数中有一个属性prototype就是原型，原型的作用：共享数据，节省内存空间，实现继承
        原型链是指实例对象和原型对象是通过__proto__原型来联系的，这种关系叫做原型链
    80.在js中不同进制数表示方式？
        二进制：0b      以0b开头
        八进制：0o      以0o开头
        十进制：
      十六进制：0x00    以0x开头
    81.typeof NaN 的结果是什么？
        结果为number。
        NaN指的是‘不是一个数字’，用于指出数字类型中的错误情况，即执行数学运算没有成功
        NaN是一个特殊值，它和自身不相等，是唯一一个非自反的值；即 NaN != NaN 为true
    82.解析字符串中的数字parseInt() 和 将字符串强制类型转换为数字Number() 的返回结果都是数字，他们有什么区别？
        解析字符串parseInt()中含有非数字字符，解析从左到右解析，解析到非数字字符时即会停止
        强制类型转换Number()不允许出现非数字字符，否则会失败返回NaN
    83.什么情况下会发生布尔值的隐式强制类型转换？
        条件判断表达式中会发生布尔值的隐式强制类型转换
        比如：1. if( .. ){}   2.  ..? '':''   3.逻辑与&&、逻辑或|| 4.do while(..) 等等
    84.如何将浮点数左边的数每三位添加一个逗号（千位分隔符）？ 如 12000000.11转化为 12,000,000.11    包含整数？
        function format(number){
            var number = number + ''
            if(number.indexOf('.')!=-1){
                return number.replace(/(?!^)(?=(\d{3})+\.)/g, ",");
            }else{
                return number.replace(/\d{1,3}(?=(\d{3})+$)/g,"$&,")
            }
        }
    85.JavaScript创建对象的几种方式？
        1.工厂模式（解决了创建多个相似对象时，代码的复用问题；没有解决对象识别的问题，即不知道对象的类型）
            function createPerson(name,age){
                var o = new Object();
                o.name = name;
                o.age = age;
                o.saiName = function(){ alert(o.name) }
                return o
            }
            var person1 = createPerson('zs',18)
            var person2 = createPerson('ls',16)
        2.构造函数模式（解决了工厂模式对象识别问题；没创建一个对象都会创建一个saiName函数的实例做相同的事，造成内存的浪费）
            function createPerson(name.age){
                this.name = name;
                this.age = age;
                this.saiName = function(){ alert(this.name) }
                return this
            }
            var person1 = new createPerson('zs',18)
            var person2 = new createPerson('ls',16)
        3.原型模式
            function Person(){}
            Person.prototype.name = 'zs'
            Person.prototype.age = 18
            Person.prototype.saiName = function(){ alert(this.name) }
            var person1 = new Person()
        4.构造函数模式+原型模式 组合
            function Person(name, age, job){
                this.name = name;
                this.age = age;
                this.job = job;
            }
            Person.prototype = {
                constructor: Person,
                sayName: function(){
                    alert(this.name);
                }
            }
            var person1 = new createPerson("james"，9，"student");
            var person2 = new createPerson("kobe"，9，"student");      
    86.JavaScript实现继承的几种方式？
        1.原型链继承
            function SuperType(){
                this.colors = ['red','green','blue']
            }
            function SubType(){}
            SubType.prototype = new SuperType()
            var subtype1 = new SubType();
            console.log(subtype1.colors.push('white'))          // 4 ['red','green','blue','white']
            var subtype2 = new SubType(); 
            console.log(subtype2.colors)                    // ['red','green','blue','white']
        2.借用构造函数继承
            function SuperType(){
                this.colors = ['red','green','blue']
            }
            function SubType(){
                SuperType().call(this)
            }
            var instance1 = new SubType()
            instance1.colors.push('yellow')
            console.log(instance1.colors)  // ['red','green','blue','yellow']
            var instance2 = new SubType()
            console.log(instance2.colors)  // ['red','green','blue']
        3.组合继承
            function SuperType(name){
                this.name = name
                this.colors = ['red','green','blue']
            }
            SuperType.prototype.sayName = function(){ console.log(this.name) }
            function SubType(name,age){
                SuperType.call(this,name)
                this.age = age
            }
            //继承方法
            SubType.prototype = new SuperType()
            SubType.prototype.constructor  = SubType

            var instance1 = new SubType("james",9);
            instance1.colors.push("black");
            console.log(instance1.colors);  //"red,green,blue,black"
            instance1.sayName(); // "james"
            var instance2 = new SubType("kobe",10);
            console.log(instance2.colors);  //"red,green"blue,
            instance2.sayName(); // "kobe"
        4.寄生式继承
            function createPerson(person){
                var clone = object(person)      // 通过调用函数创建新对象
                clone.saiHi = function(){       // 某种方式增强这个对象
                    console.log(‘hi’)
                }
                return clone            // 返回这个对象
            }
            var person1 = createPerson({name:'zs'})
            person1.saiHi()     // hi
        5.寄生式组合继承 (引用类型最理想的继承范式)
            function inheritPrototype(subType, superType) {
                //1.创建了超类（父类）原型的（副本）浅复制
                var prototype = Object.create(superType.prototype);
                // 2.修正子类原型的构造函数属性
                //  constructor属性也是对象才拥有的，它是从一个对象指向一个函数，含义就是指向该对象的构造函数
                // prototype.constructor  未修改前指向的 superType，为了弥补因重写原型而失去的默认constructor属性。 
                prototype.constructor = subType;
                // 3.将子类的原型替换为超类（父类）原型的（副本）浅复制
                subType.prototype = prototype;
            }
            function SuperType(name) {
                this.name = name;
                this.colors = ["red", "blue", "green"];
            }
            SuperType.prototype.sayName = function () {
                alert(this.name);
            };
            function SubType(name, age) {
                //构造函数式继承--子类构造函数中执行父类构造函数
                SuperType.call(this, name);
                this.age = age;
            }
            // 核心：因为是对父类原型的复制，所以不包含父类的构造函数，也就不会调用两次父类的构造函数造成浪费
            inheritPrototype(SubType, SuperType);
            SubType.prototype.sayAge = function () {
                alert(this.age);
            }
            var instance = new SubType("lichonglou");
            console.log(instance.name)
    87.JavaScript作用域链？
        作用域链的作用是保证执行环境有权访问的所有变量和函数的有效访问，通过作用域链，我们可以访问到外层环境的变量和函数。
    88.This的理解？
        this是执行上下文中的一个属性，它指向最后一次调用这个方法的对象。在实际开发中，this的指向可以通过四种调用模式来判断
        1.函数调用模式，当一个函数直接作为函数来调用时，这个this指的是window（全局对象）
        2.方法调用模式，当一个函数以一个对象的方法来调用时，这个this指的是这个对象
        3.构造器调用模式，当一个函数用new的方式调用时，函数执行前会新创建一个对象，this指向这个新创建的对象
        4.apply、call、bind调用模式，这三个方法都可以显示的指定调用函数的this指向
    89.写一个通用事件函数(封装 绑定事件、解绑事件、获取事件目标、获取event、阻止冒泡、取消默认行为)？
        绑定事件：
            function addEvent(ele,eventType,fun){
                if(ele.addEventListener){
                    ele.addEventListener(eventType,fun,false)
                }else if(ele.attachEvent){
                    ele.attachEvent('on'+eventType,fun)
                }else{
                    ele['on'+eventType] = fun
                }
            }
        解绑事件：
            function removeEvent(ele,eventType,fun){
                if(ele.removeEventListener){
                    ele.removeEventListener(eventType,fun,false)
                }else if(ele.detachEvent){
                    ele.detachEvent('on'+eventType,fun)
                }else{
                    ele['on'+eventType] = null
                }
            }
        获取事件目标:
            function getTarget(event){
                return event.target || event.srcElement;
            }
        获取event
            function getEvent(event){
                return event || window.event
            }
        阻止冒泡：
            function stopPropagation(event){
                if(event.stopPropagation){
                    event.stopPropagation()
                }else{
                    event.cancelBubble = true
                }
            }
        取消默认行为：
            function preventDefault(event){
                if(event.preventDefault){
                    event.preventDefault()
                }else{
                    event.returnValue = false
                }
            }
    90.三种事件模型是什么？ 事件处理机制
        第一种事件模型（原始事件模型）：通过onclick，on+'事件类型'的形式绑定事件，只能绑定一个相同事件，再次绑定将会覆盖之前的事件
        第二种事件模型（IE事件模型）： 通过attachEvent绑定事件，只支持冒泡，所以事件有两个阶段，目标阶段和冒泡阶段，事件从目标元素冒泡到document，并且一次检查各个节点是否绑定了事件，如果有则执行
        第三种事件模型：通过addEventListener绑定事件，有三个阶段，捕获阶段、目标阶段、冒泡阶段。捕获阶段：事件从外向内执行document->ele;冒泡阶段：事件从内向外执行ele->document;默认为冒泡（false）
    91.事件委托是什么？
        事件委托的本质上利用了浏览器事件冒泡的机制，因为事件在冒泡的过程中会上传到父节点，并且父节点可以通过事件对象获取到目标节点，
        因此可以把子节点的监听函数定义在父节点上，由父节点的监听函数统一处理多个子元素的事件，这种方式成为事件委托。
    92. ["1","2","3"].map(parseInt) 答案是什么？  
        map() 接收三个参数 item,index,arr （当前遍历项，当前索引，当前数组）
        parseInt() 接受两个参数 str,radix (要被解析的字符串，要解析的数字的基数)
        因为map()遍历第三个参数不需要，所以map之后的数组为[ parseInt(1,0), parseInt(2,1), parseInt(3,2) ]
        因为parseInt()第二个参数的值的取值范围在2~36之间且包含0，并且第一个参数的值的首位必须小于第二个参数，否则为NaN
        所以最终的结果就是 [1,NaN,NaN]
    93.闭包？
        闭包是指有权访问另一个函数作用域中变量的函数，
        闭包的作用：
         1.函数外部能够访问到函数内部的变量。通过使用闭包，在外部调用闭包函数，从而在外部访问到函数内部的变量，可以使用这种方法来创建私有变量
         2.使已经运行结束的函数上下文中的变量对象继续留在内存中，因为闭包函数保留了这个变量对象的引用，所以这个变量对象不会被回收
    94.javascript代码中的"use strict";是什么意思?使用它区别是什么？
        "use strict" 指的是一种严格运行模式，在这种模式对js的使用添加了一些限制，比如说禁止this指向全局变量，禁止使用with语句等。
    95.怎么判断一个数据属于某种数据类型？  (如何判断一个对象是否属于某个类)
        function getType(o){
            return Object.prototype.toString.call(o).slice(8,-1)
        }
    96.JavaScript中，哪个函数执行对象查找时永远不会去查找原型？
        Object.hasOwnProperty() : 用来检测一个对象是否含有特定的自身属性
    97.对于JSON的了解？
        JSON是一种基于文本的轻量级数据交换格式，它可以被任何的编程语言读取和作为数据格式来传递。
        在js中，有两个操作json的方法，JSON.stringify():通过一个符合json格式的数据结构，将其转换为一个json字符串
        JSON.parse():将一个符合json格式的字符串转换成一个js数据结构，如果不符合json格式的字符串则会报错。
    98.JS延迟加载的方式有哪些？
        1.放在body标签底部
        2.<script>标签中使用defer属性
        3.<script>标签中使用async属性
        4.动态加载script标签
    99.Ajax？ 如何创建一个ajax
        ajax是一种异步通信的方法，通过js向服务器发送http请求，然后根据服务器返回的数据，更新网页的相应部分，而不用刷新整个页面的一个方法
        var xhr = new XMLHttpRequest()                          // 1. 创建XMLHttpRequest对象
        xhr.open('get/post','url',true)                         // 2. 使用open创建一个http请求，接受三个参数 method，url，是否异步
        xhr.onreadstatechange = function(){                     // 3. 监听xhr状态的改变
            if(this.readyState !== 4) return;                   // 4. readyState等于4时，代表服务器返回的数据接收完成
            if(this.status === 200){                            // 5. status状态码200 请求成功
                console.log('请求成功')
            }else{
                console.error(this.statusText)
            }
        }       
        xhr.onerror = function(){                               // 6.监听xhr请求失败
            console.error(this.statusText)
        }
        xhr.responseType = 'json';
        xhr.setRequestHeader('Accept','application/json');
        xhr.send(null)                                          // 7. 发送请求(params)
    100.浏览器的缓存机制？
        浏览器的缓存机制指的是通过一段时间内保留已接收到web资源的一个副本，如果在资源的有效时间内，发起了对这个资源的再一次请求，那么浏览器会直接使用缓存的副本，而不是向服务器发送请求。
        浏览器的缓存类型分为两种：强缓存/协商缓存
        强缓存：
            1.不会向服务器发送请求，直接从缓存中读取资源，在谷歌控制台network选项中，可以看到请求状态码200并且size显示from disk cache或from memory cache 
        协商缓存：
            1.向服务器发送请求，服务器会根据这个请求的请求头的一些参数来判断是否为协商缓存，如果是则返回304状态码并带上新的响应头通知浏览器从缓存中读取资源
    101.ajax解决浏览器缓存问题？
        1. 在ajax发送请求前加上 ：          xhr.setRequestHeader('Cache-Control','no-cache')
        2. 在请求地址上加上随机数或时间戳 :  "/server?t="+Math.random() 或 "/server?t="+new Date().getTime()
        3. 如果是jquery，直接加上 :         $.ajaxSetup({cache:false})
    102.如何解决跨域问题？
        1. get请求的话 可以使用 jsonp
        2. CORS方式：Access-Control-Allow-Origin:*
        3. 使用websocket协议，这个协议没有同源限制
        4. 使用服务器来代理跨域的访问请求：就是有跨域的请求操作时发送请求给后端，让后端代为请求再把最后返回的结果返回到前端
    103.同源策略？
        我对浏览器的同源策略的理解是，一个域下的js脚本在未经允许的情况下，不能够访问另一个域的内容，这里的域指的是两个域的协议/域名/端口必须相同，否则则不属于同一个域。
        同源策略只要限制了三个方面：
            1.当前域下的js脚本不能够访问其他域下的cookie/localStorage和indexDB
            2.当前域下的js脚本不能够操作访问其他域下的DOM
            3.当前域下ajax不能够发送跨域请求
            同源策略的目的主要是为了保证用户的信息安全，它只是对js脚本的一中限制，并不是对浏览器的限制，对于一般的img或者<script>脚本请求都不会有跨域的限制
    104.document.write 和 innerHTML的区别？
        document.write：替代整个文档内容，会重写整个页面
        innerHTML：替代指定元素的内容，只会重写页面中的部分内容
    105.js中DOM的操作(增删改查等操作)
        查找节点：
            1.document.getElementById()
            2.document.getElementsByTagName()
            3.document.getElementsByClassName()
            4.document.getElementsByName()
            5.document.querySelector()
            6.document.querySelecrotAll()
        创建：  createElement(node)      
        追加：  appendChild(node)
        插入：  insertBefore(oldNode,newNode)
        替换：  replaceChild(oldNode,newNode)
        移除：  removeChild(node)
        克隆：  cloneNode(true)   // true:克隆节点下所有的元素  false:克隆当前节点
        属性操作：
            getAttribute(key)   setAttribute(key,value)    hasAttribute(key)    removeAttribute(key)
    106. .call() 和 .apply()的区别？
        .call()和.apply()的作用是一样的，都是为了改变this指向,第一个参数为this的指向
        .call(): 传入的参数不确定，从第二个参数开始每个参数被依次传入函数中
        .apply(): 传入两个参数，第二个参数为一个数组
    107.js中数组，对象，字符串的常用方法？
        数组：
            forEach()   遍历
            map()       遍历，返回一个新更改的数组
            filter()    遍历，过滤返回一个条件满足的数组
            every()     判断，数组元素是否全部都满足一个条件
            some()      判断，数组元素是否有一个满足条件
            find()      查找元素，找到元素则直接返回，不在进行遍历查询
            findIndex() 功能同上，不同点在于查找元素索引
            push()      在数组后面新加一个元素
            unshift()   在数组前面新加一个元素
            shift()     删除数组中第一个元素
            pop()       删除数组中最后一个元素
            concat()    拼接数组
            reverse()   数组反转
            sort()      排序
            join()      参数作为分隔符，将数组转化为字符串
            slice()     截取数组（开始的位置，结束的位置）index从0开始
            splice(下标，要删除的个数，插入或则是替换的值)
            reduce()    求和
        对象：
            hasOwnProperty()    检测对象是否含有某个属性
            Object.keys()       将对象中的属性名存在一个数组中返回
            Object.values()     将对象中的属性值存在一个数组中返回
            Object.is()         判断两个值是否相等
            Object.assign()     对象的合并（浅拷贝）
        字符串：
            replace()           替换字符串(‘要替换的字符串’，‘替换后的字符串’)
            trim()              清楚两端空格
            substr()            字符串截取（开始的位置，结束的位置）index从0开始
            split()             字符串转数组，将参数中的值进行匹配截断成为一个数组
            includes()          字符串中是否包含某些字符串
            startsWith()        字符串是否以某些字符串开头
            endsWith()          字符串是否以某些字符串结尾
            padStart()          满足条件时在字符串前面填充字符串 (字符串长度不足多少个时，要填充的字符串)
            padEnd()            满足条件时在字符串后面填充字符串 (字符串长度不足多少个时，要填充的字符串)
            repeat()            重复字符串（重复的次数）
        数组和字符串共有的方法： indexOf()和lastIndexOf()     
    108.JavaScript中的作用域和变量声明提升？
        作用域分为局部作用域和全局作用域，函数内声明的变量称为局部作用域，不可被函数外所访问；函数外声明的变量称为全局作用域，可以在任何地方使用。
        变量声明提升就涉及到一个预解析的概念，也就是说函数的声明、变量的声明会提升到使用之前，只提升声明，不提升赋值。
    109.如何编写高性能的JavaScript

    110.哪些操作会造成内存泄漏？
        1.由于使用未声明的变量（隐士全局变量），而意外的创建了全局变量，而使这个变量一直留在内存中无法被回收
        2.设置了setInterval()定时器，而没有清除掉这个定时器
        3.获取一个DOM元素的引用，而后这个元素删除了，由于我们一直保留了这个元素的引用，那么它也不会被回收
        4.不合理的闭包，从而导致某些变量一直存在于内存中
    111.需求：实现一个页面操作不会整页刷新的网站，并且能在浏览器前进、后退时正确响应。（SPA）
        第一种方式：通过location.hash，设置hash可以操作浏览器的前进和后退
        第二种方式：通过H5的pushState API,通过pushState把记录保存到浏览器的历史记录中，然后通过window.onpopstate事件来响应浏览器的前进和后退，
        这种方式有两个问题：一个是打开首页时没有记录，可以通过replaceState来将首页记录替换；另一个问题是当页面刷新的时候，请求的url需要后端配合将其重定向到一个页面中
    112.如何判断当前脚本运行在浏览器端还是node端
        this === 'window' ? 'browser' : 'node'
    113.移动端的点击事件有延迟，是多久？为什么会出现？怎么解决？
        移动端的点击事件有300ms的延迟，是因为移动端有双击缩放的这个操作，因此浏览器在click事件后要等待300ms，来判断用户是不是双击。
        解决方式：
            1.通过meta标签禁止网页的缩放
            2.通过调用现成的第三方库 比如 FastClick
    114.前端路由？ 优缺点？
        前端路由是指把不同的路由对应不同的页面的任务交给前端来做，一般在单页面应用会使用前端路由。
        优点：无刷新、用户体验好，不需要每次都从服务器全部获取，快速展现给用户
        缺点：单页面无法记住之前滚动的位置，无法在前进后退的时候回到上一次的位置
    115.检测浏览器版本的方式？
        1.通过 window.navigator.userAgent
        2.通过各个浏览器中的特征来判断，比如IE浏览器有window.ActiveXObjcet函数，谷歌浏览器中有window.chrome对象等
    116.使用js实现获取文件扩展名？
        function getFileExtension(filename) {
            return filename.slice(((filename.lastIndexOf(".") - 1) >>> 0) + 2);
        }
    117.防抖 节流？
        防抖：在事件被触发n秒后在执行回调，如果在这n秒内事件又被触发，则重新计时
            function debounce(fn,delay){
                var timer = null;
                return function(){
                    var that = this,
                        arg = arguments;
                    if(timer){
                        window.clearTimeOut(timer);
                        timer = null;
                    }
                    timer = window.setTimeOut(()=>{
                        fn.apply(that,arg);
                    },delay)
                }
            }
        节流：规定一个时间，在这个时间内，只能有一次触发事件回调函数的执行，如果在同一个时间内某事件被触发多次，只能有一次生效
            function throttle(fn,delay){
                var preTime = Date.now();
                return function(){
                    var nowTime = Date.now();
                    var that = this,
                        arg = arguments;
                // 如果两次时间间隔超过了指定时间，则执行函数
                    if(nowTime-preTime >= delay){
                        preTime = Date.now();
                        return fn.apply(that,arg)
                    }
                }
            }
    118.Object.is() 和 == 和 === 操作符的区别？
        使用双等号判断是否相等时，如果两边的类型不一致，则会进行强制类型转换后在进行比较，
        使用三等号判断是否相等时，如果两边的类型不一致，不会做强制类型转换，直接返回false，
        使用Object.is()判断时，一般情况下和使用三等号判断一致，只是新加了一些特殊处理，比如：+0和-0为false，NaN和NaN为true
    119.Unicode和UTF-8的关系？
        Unicode是一个字符集合，每一个字符对应不同的Unicode编码，它只规定了符号的二进制代码，却没有规定这个二进制代码在计算机中如何传输
        UTF-8是一种Unicode的编码格式，可以用1~4个字节表示一个字符
    120.js事件循环是什么？
        因为JS是单线程运行的，在代码执行的时候，通过将不同函数的执行上下文压入执行栈中来保证代码的有序执行。
        在执行同步代码的时候，如果遇到了异步事件，js引擎并不会一直等待其返回结果，而是会将这个事件挂起，继续执行执行栈的其他任务。
        当一个异步事件返回结果后，js会将这个事件加入到与当前执行栈不同的另一个队列，我们称之为任务队列。
        被放入事件队列不会立即执行，而是等待当前执行栈中所有的任务都执行完毕，主线程属于闲置状态时，才会去找任务队列是否有任务。
        如果有，那么主线程会从中取出排在第一位的事件，并把这个事件对应的回调放入执行栈中，然后执行同步代码。
        如此反复，就会形成一个循环，我们称之为事件循环。  
        任务队列(异步任务)可以分为宏任务和微任务，同一次事件循环中，微任务永远在宏任务之前执行。
        比如：
            1.微任务：new Promise().then() then函数的处理就是一个微任务   new Promise()是同步任务
            2.宏任务：setInterval()    setTimeout()
    121.js中深浅拷贝的实现？
        浅拷贝：
           1. function shallowCopy(object){
                // 只拷贝对象
                if(!object || typeof object !== 'object') return;
                // 根据 object 的类型判断是新建一个数组还是对象
                var newObject = Array.isArray(object) ? [] : {};
                for(var key in object){
                    if(object.hasOwnProperty(key)){
                        newObject[key] = object[key];
                    }
                }
                return newObject;
            }
          2. Object.assign()
            function shallowCopy(object){
                return Object.assign({},object);
            }
        深拷贝：
            function deepCopy(object){
                if(!object || typeof object !== 'object') return ;
                var new Object = Array.isArray(object) ? [] : {};
                for(var key in objcet){
                    if(object.hasOwnProperty(key)){
                        newObject[key] = typeof object[key] === 'object' ? deepCopy(object[key]) : object[key];
                    }
                }
                return newObject;
            }
    122.手写call、apply及bind函数？
        call: Function.prototype.myCall = function(context){
                if(typeof this != 'function'){
                    throw new TypeError('type error')
                }
                let arg = [...arguments].slice(1),
                    result = null;
                context = context || window;
                context.fn = this;
                result = context.fn(...arg)
                delete context.fn
                return result
              }  
        apply: Function.prototype.myApply = function(context){
                  if(typeof this !== 'function'){
                    throw new TypeError('type error')
                  }  
                  let result = null
                  context = context || window
                  context.fn = this
                  if(arguments[1]){
                      result = context.fn(...arguments[1])
                  }else{
                      result = context.fn()
                  }
                  delete context.fn
                  return result
               }
        bind: Function.prototype.myBind = function(context){
                if(typeof this !== 'function'){
                    throw new TypeError('error')
                }
                let arg = [...arguments].slice(1),
                    fn = this;
                return function Fn(){
                    return fn.apply(
                        this instanceof Fn ? this : context,
                        arg.concat(...arguments)
                    )
                }
              } 
    123.函数柯里化的实现？       编写一个sum函数，实现如下功能： console.log(sum(1)(2)(3))      // 6
        函数柯里化指的是一种将使用多个参数的一个函数转换成一系列使用一个参数的函数的技术
        function curry(fn, ...args) {
            return fn.length > args.length ? (...args2) => curry(fn, ...args, ...args2) : fn(...args)
        }

        function fn(a, b, c) {
            console.log(a + b + c )
        }
        sum = curry(fn)
        sum(1)(2)(3) // 6     
    124. toPrecision和toFixed和Math.round的区别？
        163.65.toPrecision(4)       =>  163.7
        163.65.toFixed(2)           =>  163.7
        Math.round(163.65)          =>  164
        toPrecision() 用于处理精度，四舍五入，从左至右第一个不为0的数开始计算 
        toFixed()   小数点后指定位数取整，从小数点开始计起（四舍五入）
        Math.round() 四舍五入取整
    125.什么是xss攻击？如何防范
        xss攻击指的是跨站脚本攻击，是一种代码注入攻击。XSS的本质是因为网站没有对恶意代码进行过滤，与正常的代码混合，导致浏览器没有办法辨别哪些脚本是可信的，从而导致了恶意代码的执行。
        xss一般分为存储型、反射型、DOM型。
        存储型：恶意代码提交到了网站的数据库中，当用户请求数据的时候，服务器将其拼接为HTML后返回给了用户，从而导致恶意代码的执行
        反射型：攻击者构建了特殊的URL，当服务器接收到请求后，从URL中读取数据，拼接到HTML后返回，从而导致恶意代码的执行
        DOM型：攻击者构建了特殊的URL，用户打开网站后，js脚本从url中获取数据，从而导致恶意代码的执行
        XSS攻击的防范可以从两个方面入手，一个是恶意代码提交的时候，一个是浏览器执行恶意代码的时候。
        对于第一个方面，由于我们没有办法判断数据最后的使用场景，所以直接在输入端进行恶意代码的处理其实是不可靠的。
        因此我们可以从浏览器的执行来进行防御：一种是使用纯前端的方式，不用服务器拼接后返回。另一种是对需要插入到HTML中的代码做好充分的转义。
        对于DOM型的攻击，主要是前端脚本的不可靠造成的，我们对于数据获取渲染和字符串拼接的时候应该对可能出现的恶意代码情况进行判断。
        还有一些方式，比如CSP(告诉浏览器哪些外部资源可以加载和执行，从而防止恶意代码的注入攻击)。
        还可以对一些敏感信息进行保护，比如cookie使用http-only，使得脚本无法获取。
        也可以使用验证码，避免脚本伪装成用户执行一些操作。
    126.什么是CSP？
        CSP指的是内容安全策略，它的本质是建立一个白名单，告知浏览器哪些外部资源可以加载和执行。我们只需要配置规则，如何拦截由浏览器自身实现。
        通常有两种方式来开启CSP，一种是设置HTTP响应头信息Content-Security-Policy,另一种是通过meta标签<meta http-equiv="Content-Security-Policy" content="（配置规则）">
    127.什么是CSRF攻击？如何防范
        CSRF：指的是跨站请求伪造攻击，攻击者诱导用户进入第三方网站，然后该网站向被攻击网站发送跨站请求。
        如果用户在被攻击网站中保存了登录状态，那么攻击者就可以利用这个登录状态，绕过后台的用户验证，冒充用户向服务器执行一些操作。
        CSRF攻击的本质是利用了cookie会在同源请求中携带发送给服务器的特点，以此来实现用户的冒充。
        一般的CSRF攻击类型有三种：
        1.GET类型的攻击：比如在网站中的一个img标签里构建一个请求，当用户打开网站时就会自动发起提交
        2.POST类型的攻击：比如说构建一个隐藏表单，当用户进入时自动提交这个表单
        3.链接类型的攻击：比如在a标签的href属性里构建一个请求，然后诱导用户去点击。
        防范的方式：在设置cookie属性的时候设置Samesite,限制cookie不能作为第三方使用，从而可以避免被攻击者使用。
        Samesite一共有两种模式，一种是严格模式，在严格模式下cookie在任何情况下都不能被第三方cookie使用；
        在宽松模式下，cookie可以被会发生页面跳转的get请求使用。
    128.什么是MVVM？比之MVC有什么区别？
        MVVM,MVC都是常见的软件架构设计模式，主要通过分离关注点的方式来组织代码结构，优化开发效率。
        MVC通过分离Model、View、Controller的方式来组织代码结构。其中View负责页面的显示逻辑，Model负责存储页面的业务数据以及对相应数据的操作；
        并且View和Model应用了观察者模式，当Model层发生改变的时候会通知有关的View层更新界面；Controller是Model层和View层的纽带，它主要负责用户与应用的响应操作。
        MVVM中的VM指的是ViewModel通过双向的数据绑定将View层和Model层自动化同步更新。当 Model 发生变化的时候，ViewModel就会自动更新；ViewModel变化了，View也会更新。
    129.vue双向数据绑定原理？
        vue采用数据劫持结合发布者-订阅者模式的方式实现双向数据绑定，通过Object.defineProperty()来劫持各个属性的getter、setter，
        在数据变动时发布消息给订阅者，触发相应的监听回调，重新渲染视图。

    130.介绍一下Object.defineProperty()
        Object.defineProperty() 作用：创建一个对象，该方法接收三个参数，第一个参数为对象名称，第二个参数为对象中的属性名称，第三个参数是该属性的描述符。
        一个属性的描述符有四个属性，分别是 value(属性的值)  writable(是否可重写)   enumerable(是否可枚举/遍历)   configurable(是否可配置)
        还有get和set存取描述符，在给对象设置get和set时，就不能设置属性值value和可读性writable，两者不能共存
        通过 Object.defineProperty添加的属性默认是不可修改、不可枚举、不可写的
    131.使用Object.defineProperty()进行数据劫持有什么缺点？
        Object.defineProperty()本身有一定的监控数组下标变化的能力，但是在Vue中，从性能、体验的性价比考虑，尤大大放弃了这个特性。
        而是内部更改了原Array的方法 push(),shift(),pop(),unshift()等，通过Vue.$set()的方式改变数组对象。
    132.什么是Virtual DOM？为什么Virtual DOM比原生DOM快？
        从我的理解来看，Virtual DOM是指生成Virtual DOM树，对比两棵树的差异，更新视图。
        其中生成虚拟DOM树围绕着三个部分，分别是：节点类型、节点属性、子节点。
        对比两棵树的差异首先需要对树进行遍历，这样每个节点都有一个唯一的标记，通过这个标记和新的树的同一位置的节点进行对比，有差异则保存到一个对象中，
        常见的差异有四种，分别是：替换节点、增删节点、修改节点属性、更改文本内容。
        更新视图分别对应四种方法：replaceChild() appendChild() removeChild() setAttribute() removeAttribute() textContent/innerText
        我认为Virtual DOM这种方法对于我们需要大量的DOM操作的时候，能够很好的提高我们的操作效率，通过在操作前确定需要做的最小修改，尽可能的减少DOM操作带来的重绘和回流的影响。
        其实Virtual DOM并不一定比我们真实操作DOM要快，这种方法的目的是为了提高我们开发时的维护性，在任意情况下，都能保证一个尽量小的性能消耗去进行操作。
        Virtual DOM的优势不在于单次的操作，而是在大量、频繁的数据更新下，能够对视图进行合理、高效的更新，这一点是原生DOM远远无法替代的。
    133.说说webpack？
        webpack的核心是一个用于现代JavaScript应用程序的静态模块绑定器。当webpack处理应用程序时，它会在内部构建一个依赖关系图，映射项目所需的每个模块，并生成一个或多个包。
        webPack有四个主要的核心概念（Entry、Output、Loader、Plugins）入口、出口、加载器、插件
        Entry：是webpack的入口起点，它指示webpack应该从哪个模块开始着手，来构建内部依赖图的开始。
        Output：打包文件的目录名称，告诉webpack在哪里输出它所创建的打包文件 默认位置为./dist
        Loader:可以理解为webpack的加载编译器，它使得webpack可以处理一些非JavaScript的文件。在对loader进行配置的时候，
            test属性标志着有哪些后缀的文件应该被处理，是一个正则表达式；use属性指定test类型的文件应该用哪个loader进行预处理
            常用的loader有 style-loader   css-loader等
        Plugins:用于执行范围更广的任务，包括打包、优化、压缩、搭建服务器等等，要使用一个插件，一般是先使用npm包管理器进行安装，然后在配置中引入，
            最后将其实例化后传递给plugins数组属性中。常用的plugins有  htmlWebpackPlugin（生成HTML） 、 uglifyjsWebpackPlugin（删除注释压缩） 等等
    134.clientWidth/Height 和 offsetWidth/Height 和 scrollWidth/Height 的区别？
        clientWidth/Height:返回的是元素内部的宽度/高度，它只包含content+padding，不包含滚动条的宽高。
        clientLeft/Top : 返回的是左边框/上边框的宽度。
        offsetWidth/Height:返回的是元素的布局宽度/高度，它包含content+padding+border，包含了滚动条的宽高。
        offsetLeft/Top : 返回的是当前元素相对于其offsetParent元素 左侧/顶部 的距离。
        scrollWidth/Height: 返回值包含content+padding+溢出内容的尺寸。
        scrollTop : 返回的是一个元素的内容垂直滚动的距离
        scrollLeft : 返回的是元素滚动条到元素左边的距离
    135.异步编程的实现方式？

    136.js和css的动画有啥区别？分别实现方式？
        css动画的优点：在性能上会稍微好一些，浏览器会对css3的动画做一些优化，代码也相对简单；缺点就是在动画控制上不够灵活，兼容性不好。
        js动画正好弥补了这两个缺点，控制能力强，可以单帧的控制，变换，同时还可以兼容，并且功能强大。对于一些复杂的动画，使用js会比较靠谱，
        而实现一些小的交互动效的时候就用css好。
        css：
            @keyframes 动画名称{
                from / 0% {}
                to / 100% {}
            }
            animation: 动画名称 动画所需时间  动画速度曲线  延迟时间  播放的次数  是否轮流反向播放
        js:
            1. 利用setTimeout 定时器的方式实现
            2. 利用setInterval 定时器的方式实现
            3. 利用requestAnimationFrame() 方法实现，将执行动画的每一步传到requestAnimationFrame中，在每次执行完后进行异步回调来连续触发动画效果。性能上比上两种方式要好
            4. 利用 canvas 画布实现
    137.get和post请求在缓存方面的区别？
        缓存一般只适用于不会修改服务器数据的请求，一般get请求都是查找请求，不会对服务器资源数据造成修改，而post请求一般都会对服务器数据进行修改，
        所以，一般会对get请求进行缓存，很少会对post请求进行缓存。
    138.图片懒加载和预加载？
        懒加载：也叫延迟加载，指的是在长网页中延迟加载图片的时机，当用户去访问时，再去加载，这样可以提高网站的首屏加载速度，提升用户体验，并且减少服务器压力；
            它适用于图片很多，页面很长的一些网站场景；懒加载的实现原理是将img标签中的src属性设置为空字符串，将图片的真实路径保存在一个自定义属性中，当页面滚动的时候，
            进行判断，如果图片进入页面可视区域内，则从自定义属性中取出真实路径赋值到src属性中，以此来实现图片懒加载。
        预加载：指的是将所需要的资源提前请求加载到本地，这样后面在需要用到时就直接从缓存中取资源。通过预加载能够减少用户的等待时间，提高用户体验。
            我了解到的预加载最常见的方式是通过js中的image对象，通过为image对象设置src属性，来实现图片的预加载。
        这两种方式都是提高网页性能的方式，两者主要的区别在于一个是提前加载，一个是延迟加载甚至不加载。懒加载对服务器有一定的缓解压力的作用，预加载则会加重服务器前端压力
    139.mouseover和mouseenter的区别？
        当鼠标移动到元素中就会触发mouseenter事件，类似mouseover，他们之间的区别在于mouseenter不会冒泡，也就是说只执行一次，而mouseover则会触发多次事件回调。
    140.js拖拽功能的实现？  
        一个元素的拖拽过程，可以分为三个步骤：第一步是鼠标按下目标元素，第二部是鼠标保持按下的状态移动鼠标，第三步是鼠标抬起，拖拽过程结束。
        这三个步骤分别对应3个事件，mousedown,mousemove,mouseup,只有在鼠标按下的状态鼠标移动我们才会执行拖拽事件，因此我们需要在mousedown事件中
        设置一个状态来标识鼠标已经按下，然后在mouseup事件中取消这个状态。在mousedown事件中我们首先要判断，目标元素是不是为拖拽元素，如果是拖拽元素，
        我们就设置状态保存这个时候鼠标的位置，然后在mousemove事件中，我们通过鼠标现在的位置和以前的位置的相对移动，来确定拖拽元素在移动中的坐标，
        最后mouseup事件触发后，清除状态，结束拖拽事件。
        #drag {
            position: absolute;
            width: 200px;
            height: 200px;
            background-color: green;
            left: 100px;
            top: 100px;
            cursor: move;
        }
        <div id="drag">*******</div>
        <script>
            var drag = document.getElementById('drag')
            drag.onmousedown = function (e) {
                var event = e || window.event,
                    dragX = e.clientX - drag.offsetLeft,    //鼠标点击物体那一刻相对于物体左侧边框的距离=点击时的位置相对于浏览器最左边的距离-物体左边框相对于浏览器最左边的距离
                    dragY = e.clientY - drag.offsetTop;
                if (drag.setCapture) {
                    drag.setCapture()
                }
                document.onmousemove = function (e) {
                    var event = e || window.event;
                    var left = e.clientX - dragX,
                        top = e.clientY - dragY;
                    if (left < 0) {
                        left = 0
                    } else if (left > window.innerWidth - drag.offsetWidth) {
                        left = window.innerWidth - drag.offsetWidth
                    }
                    if (top < 0) {
                        top = 0
                    } else if (top > window.innerHeight - drag.offsetHeight) {
                        top = window.innerHeight - drag.offsetHeight
                    }
                    drag.style.left = left + 'px'
                    drag.style.top = top + 'px'
                }
                document.onmouseup = function (e) {
                    this.onmousedown = null
                    this.onmousemove = null
                    if (drag.releaseCapture) {
                        drag.releaseCapture()
                    }
                }
            }
        </script>
    141.let和const的注意点？
        let: 1.不存在变量声明提升
             2.存在暂时性死区（只要块级作用域内存在let命令，它所声明的变量就绑定了这个区域，不再受外界影响）
             3.不允许重复声明
        const: 1.声明常量必须赋值，否则报错
               2.也存在暂时性死区（只能在声明的位置后面使用。）
               3.不允许重复声明，不存在声明提升
               4.常量不允许被修改，（只针对于基本数据类型，引用数据类型除外）
    142.Set和WeakSet？
        ES6提供了新的数据结构Set，它类似于数组，但是成员的值都是唯一的，没有重复的值，(一般用来做数组去重)。
        WeakSet和Set类似，也是不重复值得集合，但是WeakSet的成员只能是对象，不能是其他类型的值。WeakSet不能遍历
        WeakSet的对象都是弱引用，因此适合临时存放一组对象以及存放跟对象绑定的信息，只要这些对象在外部消失，它在WeakSet的引用也会消失。
    143.Map和WeakMap？
        Map数据结构类似于对象，也是键值对的集合，但是键的范围不限于字符串，各种类型的值都可以当作键。
        WeakMap类似于Map，也是键值对的集合，但是键只能是对象（null除外），不接受其他类型的值作为键名。
        WeakMap 弱引用的只是键名，而不是键值。键值依然是正常引用。
    144.什么是Proxy？
        Proxy用来修改某些操作的默认行为，等同于在语言层面上进行修改，所以属于一种“元编程”，即对编程语言进行编程。
        Proxy可以理解成，在目标对象之前架设一层拦截，外界对这个对象的访问都必须先通过这层拦截，因此提供了一层机制，
        可以对外界的访问进行过滤和改写。
    145.什么是Promise对象？ 什么是Promise/A+规范 ？
        Promise对象是异步编程的一种解决方案；Promise/A+规范是javascript Promise的标准，规定了一个Promise所必须具有的特性。
        Promise是一个构造函数，接收一个函数作为参数，返回一个Promise实例。一个Promise实例有三种状态，分别是：pending、resolved、rejected，
        分别代表了进行中、已成功、已失败。实例的状态只能由pending转变为resolved或者rejected状态，并且状态一经改变，就无法在被更改了。
        状态的改变是由resolve()和reject()函数来实现的。我们可以在异步操作结束后调用这两个函数改变Promise实例的状态，它的原型上定义了一个then方法，
        使用这个then方法可以为两个状态的改变注册回调函数，这个回调函数属于微任务，会在本轮事件循环的末尾进行。
    146.手写一个Promise

    147.怎么做JS代码的error统计？
        通过window.onerror事件进行统计，需要放在所有js脚本前执行
    148.Vue的生命周期是什么？ 有哪些阶段？
        Vue的生命周期指的是组件从创建到销毁的一系列过程，称之为Vue生命周期。有8个阶段（创建前，创建后，加载前，加载后，更新前，更新后，销毁前，销毁后）
        每个阶段都对应了一个生命周期的钩子函数。
        beforeCreate    组件创建前
        created         组件已创建（此时已经可以拿到data中的数据了）
        beforeMount     虚拟DOM渲染前
        mounted         虚拟DOM已渲染
        beforeUpdate    数据更新时调用（适合在更新之前访问现有的 DOM，比如手动移除已添加的事件监听器）   
        updated         数据更改导致DOM重新渲染后调用（一般不在这里做什么操作而是用watch或computed）
        beforeDestroy   实例销毁前（一般会在这里销毁定时器，解绑全局事件）
        destroyed       实例销毁后，Vue实例中所有的东西都会被移除
        处于 keep-alive 缓存的组件还有两个生命周期并且不会被销毁而是执行了这两个生命周期，activated和deactivated，分别代表 缓存的组件被激活、缓存的组件被停用
    149.Vue组件间的传递方式？
        父子组件间通信：
            1.子组件通过props属性来接收父组件的数据；然后父组件在子组件上注册监听事件，子组件通过$emit触发事件来向父组件发送数据
            2.通过ref属性给子组件设置一个名字，父组件通过$refs组件名来获得子组件；子组件可以通过$parent来获得父组件
            3.使用provider/inject，在父组件中通过provider提供变量，在子组件中通过inject来将变量注入到组件中。无论子组件有多深，只要调用了inject就可以注入provider中的数据
        兄弟组件间通信：
            1.通过EventBus的方式，本质就是创建一个空的Vue实例来作为消息传递的对象，通过在这个实例上监听$on和触发$emit事件，来实现消息的传递
            2.通过$parent.$refs来获取兄弟组件进行通信
        任意组件间通信：
            业务不复杂的情况下也可以使用EventBus的方式，不然就用Vuex
    150.computed和watch的差异？
        1.computed是计算一个新的属性，并将该属性挂载到Vue实例上；而watch是监听已经存在并挂载到Vue实例上的数据，所以用watch同样可以监听computed计算属性的变化
        2.computed本质是一个惰性的观察者，具有缓存性，只有当依赖发生变化后，第一次访问computed属性才会计算新的值；而watch则是当数据发生变化便会调用执行函数，methods也是
        3.从使用场景上说，computed适用于一个数据被多个数据影响，watch适用于一个数据影响多个数据
    151.vue-router中导航钩子函数（导航守卫）
        全局守卫：beforeEach 和 afterEach
            beforeEach((to,from,next)=>{})前置守卫： 接收三个参数，to：要进入的路由对象，from：要离开的路由对象，next是一个必须要执行的函数，
            该函数（next）如果不传参数，那就执行下一个钩子函数，如果传入false，则终止跳转，如果传入一个路径，则导航到对应的路由，如果传入error，则导航终止，error传入错误的监听函数。
            afterEach((to,from)=>{}) 后置守卫： 和前置守卫不同的就是不用传入next
        路由独享守卫：beforeEnter
            路由配置上进行定义，和beforeEach的方法参数是一样的
        组件内守卫：beforeRouteEnter    beforeRouteUpdate   beforeRouteLeave
            组件内进行定义，和beforeEach的方法参数是一样的
    152. $route 和 $router 的区别？
        $route:是路由信息对象，包括path、params、query、hash、name等路由信息参数，
        $router:是路由实例对象包括了路由的跳转方法，钩子函数等（push() replace() go() back() ...）
    153.Vue常用的修饰符？
        事件修饰符：
            .stop           阻止冒泡
            .prevent        取消默认事件
            .capture        使用事件捕获机制
            .once           只执行一次
            .self           操作当前元素才执行
            .native         监听组件根元素事件
        v-model修饰符：
            .trim           清空两端空字符
            .number         转换成数字类型
            .lazy           延迟更新，让数据失去焦点时更新
    154.Vue中key的作用？
        key的作用可以分为两种情况，第一种是v-if中使用key，第二种是v-for中使用key
        v-if:由于Vue会尽可能高效的渲染元素，通常是复用已有元素而不是从头开始渲染。因此，当我们使用v-if来实现元素切换的时候，如果切换前后含有相同类型的元素，
            那么这个元素就会被复用。如果是相同的input元素，那么切换前后用户的输入不会被清除掉，可以使用key来标识一个唯一元素，这种情况下，使用key的元素不会被复用。
            这个时候key的作用是用来标识一个独立的元素。
        v-for:用v-for更新已渲染过的元素列表时，它默认使用“就地复用”的策略。如果数据项的顺序发生了改变，Vue不会移动DOM元素来匹配数据项的顺序，而是简单复用此处的每个元素，
            因此通过为每一个列表项提供一个key值，以便跟踪元素所在的位置，从而高效的实现复用。这个key的作用是为了高效的更新渲染虚拟DOM
    155.keep-alive组件有什么作用？
        具有缓存组件的作用：如果你需要在组件切换的时候，保存一些组件的状态防止多次渲染，就可以使用keep-alive组件包裹需要保存的组件。
    156.Vue中mixin和mixins的区别？
        minxin：用于全局混入，会影响到每个组件实例。
        minxins：最常使用的扩展组件的方式了，如果多个组件中有相同的业务逻辑，就可以将这些逻辑剥离出来，通过minxins混入代码，比如返回上一个页面，上拉下拉等操作，
        需要注意的是minxins混入的钩子函数会先于组件内的钩子函数执行，并且在遇到同名选项时也会有选择性的进行合并
    157.开发中几种常用的Content-Type ?
        1. application/x-www-form-urlencoded        原生form表单
        2. multipart/form-data                      常见post提交方式 文件上传
        3. application/json                         JSON字符串
        4. text/xml                                 用来提交xml格式的数据
    158.如何封装一个javascript的类型判断函数？
        function isType(o){
            return Object.prototype.toString.call(o).slice(8,-1)
        }
    159.如何判断一个对象是否为空对象？
        Object.keys(o).length === 0
    160.手写一个jsonp？
        function jsonp(url, params, callback) {
            let querystring = url.indexOf('?') === -1 ? '?' : '&'   // 判断是否含有参数
            // 添加参数
            for (var k in params) {
                if (params.hasOwnProperty(k)) {
                    querystring += k + '=' + params[k] + '&'
                }
            }
            // 处理回调函数名
            let random = Math.random().toString().replace('.', ''),
                callbackName = "myJsonp" + random;
            // 添加回调函数
            querystring += 'callback=' + callbackName
            // 构建请求
            let scriptNode = document.createElement("script")
            scriptNode.src = url + querystring
            window[callbackName] = function () {
                // 调用回调函数
                callback(...arguments)
                // 删除这个引入的脚本
                document.getElementsByTagName('head')[0].removeChild(scriptNode)
            }
            // 发起请求
            document.getElementsByTagName('head')[0].appendChild(scriptNode)
        }
    161.手写一个观察者模式？
        var events = (function () {
            var topics = {};
            return {
                // 注册监听函数
                subscribe: function (topic, handler) {
                    if (!topics.hasOwnProperty(topic)) {
                        topics[topic] = [];
                    }
                    topics[topic].push(handler);
                },
                // 发布事件，触发观察者回调事件
                publish: function (topic, info) {
                    if (topics.hasOwnProperty(topic)) {
                        topics[topic].forEach(function (handler) {
                            handler(info);
                        });
                    }
                },
                // 移除主题的一个观察者的回调事件
                remove: function (topic, handler) {
                    if (!topics.hasOwnProperty(topic)) return;
                    var handlerIndex = -1;
                    topics[topic].forEach(function (item, index) {
                        if (item === handler) {
                            handlerIndex = index;
                        }
                    });
                    if (handlerIndex >= 0) {
                        topics[topic].splice(handlerIndex, 1);
                    }
                },
                // 移除主题的所有观察者的回调事件
                removeAll: function (topic) {
                    if (topics.hasOwnProperty(topic)) {
                        topics[topic] = [];
                    }
                }
            };
        })();
        //主题监听函数
        var handler = function (info) {
            console.log(info);
        }
        //订阅hello主题
        events.subscribe('hello', handler);
        //发布hello主题
        events.publish('hello', 'hello world');
    162.js命名规则？
        开头字符必须是字母、下划线或 $符号, 余下的字符可以是下划线、$、或任意字母或数字字符 
    163.Object.assign()
       Object.assign(target,source1,source2...) 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象，它将返回目标对象 (浅拷贝)
    164.Math.ceil() 和 Math.floor()
        Math.ceil()：向上取整      Math.floor()：向下取整
    165.如何查找一篇英文文章出现频率最高的单词？
        function findMostWord(article) {
            if (!article) return false
            article = article.trim().toLowerCase()      // 清除两端空字符串并转为小写
            let wordlist = article.match(/[a-z]+/g),   // 将文章中的每个单词转换成数组中的每一项元素
                visited = [],
                maxNum = 0,
                maxWord = '';
            article = " " + wordlist.join(" ") + " ";
            // 遍历判断单词出现次数
            wordlist.forEach(function (item) {
                if (visited.indexOf(item) === -1) {
                    visited.push(item)
                    let wrod = new RegExp(" " + item + " ", "g"),       // 正则  全局匹配 当前单词                  
                        num = article.match(wrod).length;           // 当前单词出现的个数                  
                    if (num > maxNum) {
                        maxNum = num
                        maxWord = item
                    }
                }
            })
            return maxWord + " " + maxNum
        }
    166. 算法知识总结
      (1).冒泡排序
      (2).选择排序
      (3).插入排序
      (4).希尔排序
      (5).归并排序
      (6).快速排序
      (7).推排序
      (8).基数排序

      动态规划 爬楼梯问题->（有一座高度是10级台阶的楼梯，从下往上走，每跨一步只能向上1级或者2级台阶。要求用程序来求出一共有多少种走法？）
        function getClimbingWays(n){
            if(n<1) return 0;
            if(n===1) return 1;
            if(n===2) return 2;
            lat a = 1,
                b = 2,
                temp = 0;
            for(let i = 3;i<=n;i++){
                temp = a + b;
                a = b;
                b = temp;
            }
            return temp;
        }
    167.js实现一个函数，完成超过范围的两个大整数相加功能
        function bigNumberAdd(str1, str2) {
            let result = "", // 保存最后结果
            carry = false; // 保留进位结果
            // 将字符串转换为数组
            str1 = str1.split("");
            str2 = str2.split("");
            // 当数组的长度都变为 0，并且最终不再进位时，结束循环
            while (str1.length || str2.length || carry) {
            // 每次将最后的数字进行相加，使用~~的好处是，即使返回值为 undefined 也能转换为 0
            carry += ~~str1.pop() + ~~str2.pop();
            // 取加法结果的个位加入最终结果
            result = carry % 10 + result;
            // 判断是否需要进位，true 和 false 的值在加法中会被转换为 1 和 0
            carry = carry > 9; }
            // 返回最终结果
            return result;
        }
    168.JS如何实现数组扁平化（将一个多维数组变成一个一维数组）  [1, [2, 3, [4, 5]]]  ------>    [1, 2, 3, 4, 5]
        function flattenArray(array){
            if(!Array.isArray(array)) return false;
            let result  = []
            result =  array.reduce(function(prev,cur){
                return prev.concat(Array.isArray(cur)? flattenArray(cur) : cur)
            },[])
            return result;
        }
    169.JS实现数组去重？
        function unique(arr){
            if (Array.hasOwnProperty('from')) {
                return Array.from(new Set(arr));
            } else {
                var n = {}, r = [];
                for (var i = 0; i < arr.length; i++) {
                    if (!n[arr[i]]) {
                        n[arr[i]] = true;
                        r.push(arr[i]);
                    }
                }
                return r;
            }
        }
    170.求数组的最大值 和 最小值？
        function arrayMax(arr){
            return Math.max.apply(null,arr)
        }
        function arrayMin(arr){
            return Math.min.apply(null,arr)
        }
    171.如何求两个数的最大公约数？   最小公倍数？
        最大公约数： 用大的数去除以小的那个数，然后再用小的数去除以的得到的余数，一直这样递归下去，直到余数为 0 时，最后的被除数就是两个数的最大公约数
            function getMaxCommonDivisor(a, b) {
                if (b === 0) return a;
                return getMaxCommonDivisor(b, a % b);
            }
        最小公倍数：将两个数相乘，然后除以它们的最大公约数
            function getMinCommonMultiple(a, b){
                return a * b / getMaxCommonDivisor(a, b);
            }
    172.判断一个字符串是否为回文字符串？
        function isPalindrome(str){
            let reg = /[\W_]/g,         // 匹配所有非单词的字符及下划线
            newStr = str.replace(reg,"").toLowerCase()      // 替换为空字符并转为小写字母
            reverseStr = newStr.split("").reverse().join("")    // 将字符串反转
            return reverseStr === newStr;
        }
    173.状态码？
        1xx ： 代表服务器接收到请求     
        2xx ： 代表成功         200：成功       
        3xx :  代表重定向       301：永久性重定向       302：临时性重定向       304：所请求的资源未修改
        4xx ： 代表客户端错误   400：客户端请求语法错误     404：请求资源不存在
        5xx :  代表服务器端错误     500：服务器内部错误
    174.post 和 get 请求的区别？
        post和get都是http请求的两种方法
        get请求一般用于不会对服务器资源产生影响的场景，浏览器一般会对get请求缓存，get请求的url会被保存在历史记录中，不太安全，并且浏览器对url有一个长度的限制，所以会影响get请求发送数据时的长度。
        post请求一般用于会对服务器资源产出影响的场景，浏览器很少会对post请求缓存，post请求相对安全，请求参数支持更多的数据类型。
    175.当你在浏览器中输入Google.com并且按下回车之后发生了什么？
        1.首先会对url进行解析，分析所需要使用的传输协议和请求的资源路径，如果输入的url中的协议或者主机名不合法，将会把地址栏中输入的内容传递给搜索引擎。
            如果没有问题，浏览器会检测url中是否出现了非法字符，如果存在非法字符，则对非法字符进行转义后再进行下一个过程
        2.浏览器会判断所请求的资源是否在缓存里，如果请求的资源在缓存里并没有失效，那么就直接使用，否则向服务器发起新的请求。
        3.下面是TCP建立连接的三次握手的过程，首先是客户端向服务器发送一个连接请求，服务端接收到请求后向客户端发送一个应答，客户端收到应答后也向服务器发送了一个应答，此时双方的连接就建立起来了。
        4.当页面请求发送到服务器端后，服务器会返回一个html文件作为响应，浏览器接收到响应后，开始对html进行解析，开始页面的渲染过程。
        5.浏览器首先会对html文件构建DOM树，根据解析到的css构建CSSOM树，如果遇到script标签，则判断是否含有defer或async属性，要不然script脚本的加载和执行会造成页面渲染的阻塞，
            当DOM树和CSSOM树建立好后，根据它们来建构渲染树，渲染树构建好后，会根据渲染树来进行布局，布局完成后，浏览器对页面进行绘制，这个时候整个页面就出来了
    176.http中的OPTIONS请求方法有什么作用？
        OPTIONS会请求服务器返回该资源所支持的所有http请求方法，该方法会用*来代替资源名称向服务器发送OPTIONS请求，可以测试服务器功能是否正常。
        JS的XMLHTTPRequest对象进行CORS跨域资源共享时，对于复杂请求，就是使用OPTIONS方法发送预请求，以判断是否有对指定资源的访问权限
    177.怎么实现多个网站之间共享登录状态？
        在多个网站之间共享登录状态指的就是单点登录。多个应用系统中，用户只需要登录一次就可以访问所有相互信任的应用系统。
        我认为单点登录可以这样来实现，首先将用户信息的验证中心独立出来，作为一个单独的认证中心，该认证中心的作用是判断客户端发送的账号密码的正确性，
        然后向客户端返回对应的用户信息，并且返回一个由服务器端秘钥加密的登录信息的 token 给客户端，该token 具有一定的有效时限。
        当一个应用系统跳转到另一个应用系统时，通过 url 参数的方式来传递 token，然后转移到的应用站点发送给认证中心，认证中心对 token 进行解密后验证，
        如果用户信息没有失效，则向客户端返回对应的用户信息，如果失效了则将页面重定向会单点登录页面。















*/
